#include "sudoku.h"
#include "evaluationFns.h"
#include "initialise.h"
#include "otherFns.h"
#include <cstring>
#ifdef USE_HEURISTIC_SELECTION_LIBRARY
#include "moveHeuristicSelection.h"
#endif

using namespace std;

int main(int argc, char **argv)
{
	if(argc <=1) {
		cout<<"USAGE: inFileName rSeed TLgFile RLgFile TimeLimit"<<endl;
		cout<<"\nThe Input file must be in the specified format. \nrSeed is the random seed and should be an integer\nTimeLimit is measured in CPU seconds and should be an integer\n\n";
		exit(1);
	}
	
	clock_t timeLimit = atoi(argv[5]);
	
	//RANDOM SEED
	srand(atoi(argv[2]));

	// We define our input and output streams	
	ifstream inStream;
	char inFileName[40], outFileName[40];
	strcpy(inFileName,argv[1]); strcpy(outFileName,argv[1]);
	strcat(outFileName,".sln");
	inStream.open(inFileName);
	if(inStream.fail())	{
		cout << "There was an error opening this file. Sorry.\n";
		exit(1);
	}

	//VARIABLES
	int i,r1,r2,c1,c2, r, c, sqLen, grLen, nSq,temp,newScore,score;
	int r1Sc, r2Sc, c1Sc, c2Sc, first, last, costChange, badMChainCnt, numUnfixed=0;
	double prob;

	//READ THE INPUT FILES
	inStream>>sqLen;
	nSq = sqLen;
	grLen = sqLen*sqLen;
	inStream>>first;
	last = first+grLen;
	//Read in the puzzle from the input file. -1s in the file mean that the cell is undefined (i.e. blank);
	int **grid = new int* [grLen];
	for(i=0;i<grLen;i++) grid[i] = new int [grLen];
	bool **fixed = new bool* [grLen];
	for(i=0;i<grLen;i++) fixed[i] = new bool [grLen];
	for(r=0;r<grLen;r++){
		for(c=0;c<grLen;c++){
			inStream>>temp;
			grid[r][c]=temp;
			if(grid[r][c]==-1) {
				fixed[r][c]=false;
				numUnfixed++;
			}
			else fixed[r][c] = true;
		}
	}
	inStream.close();
	




	// now we have done the tedious stuff, we start the clock
	clock_t clockStart = clock();
	clock_t clockFinish = clockStart + (timeLimit*CLOCKS_PER_SEC);

	//printGrid(grid,sqLen,grLen,first,fixed);

	//FIRST CHECK THAT THE GRID IS LEGAL AND, IF A SQUARE IS FULL EXCEPT FOR ONE SQUARE,
	//THEN FILL THAT SQUARE TO AVOID INFINITE LOOPS. (THIS WILL HAPPEN WITH THIS N. OP)
	bool valid = gridOK(grid,sqLen,grLen,first,fixed);
	if(!valid){
		cout<<"INPUT FILE NOT VALID"<<endl;
		exit(1);
	}

	//WE DO A QUICK CHECK HERE - IF THE GRID OK PROCEDURE HAS RETURNED A FULL
	//GRID, THEN WE SIMPLY EXIT HERE
	int unfixedCnt = 0;
	for(r=0;r<grLen;r++){
		for(c=0;c<grLen;c++){
			if(!fixed[r][c])unfixedCnt++;
		}
	}
	bool foundAnswerOrOutOfTime;
	if(unfixedCnt<=0)foundAnswerOrOutOfTime=true;
	else foundAnswerOrOutOfTime = false;	

	cout<<"Valid Initial Grid ... "<<endl;
	printGrid(grid,sqLen,grLen,first,fixed);

	
	//------------------------------------------------------------------------------------------------
	//We now get ready for the main part of the algorithm
	int *rowScores = new int [grLen];
	int *colScores = new int [grLen];

	double coolRate = 0.99;
	int markovChainLength = (int)pow(numUnfixed,2);
	double temperature;
	int badMChainLimit = 30;
	int restartCnt = 0;

#ifdef USE_HEURISTIC_SELECTION_LIBRARY
        MoveSelector mhs(grid, fixed, grLen, sqLen); 
#endif
	while(!foundAnswerOrOutOfTime){
		
		cout<<"(re)starting Alg"<<endl;
		
		//Randomly fill the grid
		fillGrid(grid,fixed,sqLen,grLen,nSq,first,last);
		printGrid(grid,sqLen,grLen,first,fixed);
	
		//calculate the initial temperature 
#ifndef USE_HEURISTIC_SELECTION_LIBRARY
		temperature = calcInitTemp(grid,sqLen,grLen,rowScores,colScores,first,fixed);
#else
		temperature = calcInitTemp(grid,sqLen,grLen,rowScores,colScores,first,fixed, mhs);
#endif

		//Now evaluate the grid
		score = evaluate(grid,sqLen,grLen,rowScores,colScores,first);
		cout<<"Intial Score = "<<score<<endl;
		if(score==0)foundAnswerOrOutOfTime=true;
		
		bool stuck = false;
		badMChainCnt = 0;

		//THE SA ALGORITHM IS NOW PERFORMED HERE 
		while(!foundAnswerOrOutOfTime && (temperature > 0.000001) && !stuck){
			int cnt=0, succCnt=0;
			int scoreAtMCStart = score;
			
			//Do one MarcovChain at the current temperature
			while(cnt<markovChainLength && !foundAnswerOrOutOfTime){
				
				//Do move and recalculate the score
#ifndef USE_HEURISTIC_SELECTION_LIBRARY
				doMove(grid,fixed,grLen,sqLen,r1,r2,c1,c2);
#else
				doMove(grid,fixed,grLen,sqLen,r1,r2,c1,c2, mhs);
#endif
				newScore=reEvaluate(grid,rowScores,colScores,grLen,r1,r2,c1,c2,r1Sc,r2Sc,c1Sc,c2Sc,first);
				
				//Accept according to the metropolis criteria
				costChange = newScore-score;
				prob = pow(2.718282, (-(costChange)/temperature));
				if(newScore<=score || (prob > (rand()/double(RAND_MAX))) ){
					//move is accepted
					score = newScore;
					succCnt++;
				}
				else {
					//otherwise we reset the move
					undoMove(grid,rowScores,colScores,grLen,r1,r2,c1,c2,r1Sc,r2Sc,c1Sc,c2Sc);
				}
				
				//check if we should finish
				if(score==0 || (clock()>=clockFinish) ){
					foundAnswerOrOutOfTime = true;
				}
				
				cnt++;

			}//END OF M.CHAIN
			
			//Check to see if there were improvements during the M.chain. Stop if necesarry
			if(score >= scoreAtMCStart) badMChainCnt++;
			else badMChainCnt = 0;
			if(badMChainCnt >= badMChainLimit) stuck = true;

			//update the temperature because a marcov chain has been completed
			temperature = temperature * coolRate;
			cout<<score<<"\t\t( temp = "<<temperature<<", successRate = "<<(succCnt/double(cnt))*100<<"%)"<<endl;
		
		}//END OF SA BIT 
		
		//if we didn't find the answer, reset the grid, and repeat
		if(score>0)	{
			resetGrid(grid,fixed,grLen);
			restartCnt++;
		}
	}
	
	
	//------------------------------------------------------------------------------------
	//We only get here if we have found a solution or we have run out of time
	printGrid(grid,sqLen,grLen,first,fixed);

	
	//******************************************************************
	//NOW WRITE SOME DETAILS ABOUT THE RUN TO THE MASTER LOG FILE THING
	FILE *tStream, *rStream;
	if( (tStream = fopen(argv[3], "a" )) == NULL ){
		printf( "The TimeLog File was not opened\n" );
		exit(1);
	}

	if( (rStream = fopen(argv[4], "a" )) == NULL ){
		printf( "The RestartLogFile file was not opened\n");
		exit(1);
	}

	clockFinish = clock();
	double duration;
	duration = (clockFinish-clockStart)/(double)CLOCKS_PER_SEC;
	fprintf(tStream,"%f\n",duration);   
	fprintf(rStream,"%d\n",restartCnt);

   	// Close stream 
   	if(fclose(tStream)){
		printf( "The timeLog file was not closed\n" );
		exit(1);
	}
	if( fclose(rStream) ){
		printf(" The restart log file was not closed\n");
		exit(1);
	}

	//and output the best solution found to a file
	ofstream Os;
	Os.open("theAnswer.txt");
	Os<<sqLen<<endl;
	Os<<first<<endl;
	for(r=0;r<grLen;r++){
		for(c=0;c<grLen;c++){
			Os<<grid[r][c]<<'\t';
		}
		Os<<endl;
	}
	Os.close();
	//******************************************************************

	// We now close the files and end the prog
	delete [] rowScores;
	delete [] colScores;
	return (0);
}



